package com.futuristik.modes;

import java.util.ArrayList;
import java.util.List;

import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.ContactListener;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.Manifold;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Button;
import com.badlogic.gdx.scenes.scene2d.ui.ClickListener;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Button.ButtonStyle;
import com.badlogic.gdx.scenes.scene2d.ui.Image.Scaling;
import com.futuristik.arkitek.ArkitekRenderer;
import com.futuristik.arkitek.Character;
import com.futuristik.arkitek.Map;
import com.futuristik.objects.ArkitekObject;
import com.futuristik.objects.Edge;
import com.futuristik.objects.Flag;
import com.futuristik.objects.Flag.FlagType;

public class Play extends ArkitekMode implements InputProcessor {
	World world;
	
	Map map;
	ArkitekRenderer renderer;
	boolean enablePause = false;
	Skin skin;
	Stage ui;
	
	List<Body> obstacleBodyList;
	
	Character player;
	SpriteBatch playerBatch = new SpriteBatch(1);
	
	OrthographicCamera cam;
	
	public Play(Game game) {
		super(game);
	}

	public Play(Game game, Map map) {
		this(game, map, false);
	}
	
	public Play(Game game, Map map, boolean PLAY){
		super(game);
		this.map = map;
		enablePause = PLAY;
	}

	@Override
	public void show() {
		world = new World(new Vector2(0, -20), true);
		cam = new OrthographicCamera(24, 16);
		
		if (map == null){
			map = new Map();
		}
			
		int count = 1;
		while(Gdx.files.external("arkitekMaps/map"+count+".txt").exists()){
			count++;
		}
		
		if(enablePause) map.save();
		
		map.setupWorld(world);
		
		obstacleBodyList = new ArrayList<Body>();
		for(ArkitekObject obj : map.getObjects()){
			obstacleBodyList.add(obj.getBody());
		}
		
		world.setContactListener(new ContactListener(){

			@Override
			public void beginContact(Contact contact) {
				Fixture A = contact.getFixtureA();
				Fixture B = contact.getFixtureB();
				
				int i = obstacleBodyList.indexOf(A.getBody());
				if(i > -1 && B.getBody().equals(player.getBody())){
					ArkitekObject match = map.getObjects().get(i);
					match.playerContact(player, contact);
				}
				
				for(Edge e : map.worldEdges){
					//System.out.println(contact.getWorldManifold().getNormal()+" "+new Vector2(0, 1)+" "+contact.getWorldManifold().getNormal().equals(new Vector2(0f, 1f)));
					if(e.box.equals(A.getBody()) && contact.getWorldManifold().getNormal().angle() == (new Vector2(0f, 1f)).angle()){
						player.state = Character.DYING;
						player.stateTime = 0f;
					}
						
				}
				
				/*
				int j = obstacleBodyList.indexOf(B.getBody());
				if(j > -1 && A.getBody().equals(player.getBody())){
					ArkitekObject match = map.getObjects().get(j);
					match.playerContact(player, contact);
				}
				*/
				//System.out.println("A: "+A.getBody()+" "+i+" B:"+B.getBody()+" "+j+" Player: "+player.getBody());
			}

			@Override
			public void endContact(Contact contact) {}

			@Override
			public void preSolve(Contact contact, Manifold oldManifold) {
				Fixture A = contact.getFixtureA();
				Fixture B = contact.getFixtureB();
				
				int i = obstacleBodyList.indexOf(A.getBody());
				if(i > -1 && B.getBody().equals(player.getBody())){
					ArkitekObject match = map.getObjects().get(i);
					if(match.prePlayerContact(player, contact, oldManifold) && player.state != Character.DYING)
						game.setScreen(new WinMode(game));
				}
			}
			
			//	if(match.prePlayerContact(player, contact, oldManifold)){
			//	game.setScreen(new WinMode(game));
			

			@Override
			public void postSolve(Contact contact, ContactImpulse impulse) {}
		
		});

		player = new Character(world);

		//This finds the flag that determines the spawn location and spawns the character in that spot.
		List<Flag> mapFlags = map.getFlags();
		for(Flag foo : mapFlags){
			if(foo.getType().equals(FlagType.SPAWN)){
				player.lastCheckpoint = foo;
			}
		}
		
		renderer = new ArkitekRenderer(world, cam, map);
		ui = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true);
		skin = new Skin(Gdx.files.internal("data/uiskin.json"),
				Gdx.files.internal("data/uiskin.png"));
		
		//This generates the Width and Height of the panels in the User Interface
		//It is created based on the width and height of the viewport of the game and so will scale accordingly.
		float panelWidth = Gdx.graphics.getWidth() / 6f;
		float panelHeight = Gdx.graphics.getHeight();
		
		/*
		 * USER INTERFACE CREATION
		 */
		
		// LEFT BUTTON
		Button leftPanel = new Button("<", skin.getStyle(ButtonStyle.class),
				"leftPanel");
		// Button newGameButton = new Button(new Image(new TextureRegion(new
		// Texture(Gdx.files.internal("data/menu2.png")), 22, 157, 278, 37)),
		// skin.getStyle(ButtonStyle.class));
		leftPanel.y = 0;
		leftPanel.x = 0;
		leftPanel.width = panelWidth;
		leftPanel.height = panelHeight;
		
		// RIGHT BUTTON
		Button rightPanel = new Button(">", skin.getStyle(ButtonStyle.class),
				"rightPanel");
		// Button rightPanel = new Button(new Image(new TextureRegion(new
		// Texture(Gdx.files.internal("data/menu2.png")), 22, 157, 278, 37)),
		// skin.getStyle(ButtonStyle.class));
		rightPanel.y = 0;
		rightPanel.x = 5 * panelWidth;
		rightPanel.width = panelWidth;
		rightPanel.height = panelHeight;
		
		
		// PAUSE BUTTON
		// enablePause is true when we have arrived in Play mode via Creation Mode and allows for 
		// the ability to transfer the GameMode back to Creation Mode.
		Button pauseButton = null;
		if (enablePause) {
			leftPanel.height = 3 * panelHeight / 4;
			pauseButton = new Button(new Image(new TextureRegion(new Texture(
					Gdx.files.internal("data/Button-Pause-icon.png")), 0, 0,
					256, 256), Scaling.fit, "PauseButton"), new ButtonStyle());
			pauseButton.y = Gdx.graphics.getHeight() - panelHeight / 4;
			pauseButton.width = panelWidth;
			pauseButton.height = panelHeight / 4;
			pauseButton.setClickListener(new ClickListener() {
				@Override
				public void click(Actor actor) {
					// map.world.destroyBody(map.world.);
					game.setScreen(new Creation(game, map));
				}
			});
		}
		ui.addActor(leftPanel);
		ui.addActor(rightPanel);
		if (enablePause)
			ui.addActor(pauseButton);
		
		/*
		 * END USER INTERFACE CREATION
		 */
		
		Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
		Gdx.input.setInputProcessor(this);
	}

	@Override
	public void render(float delta) {		
		Vector2 pos = player.getBody().getPosition();
		
		if(player.state == Character.SPAWN){
			player.spawnAtFlag(player.lastCheckpoint);
			player.state = Character.IDLE;
		}
		
		delta = Math.min(0.03f, delta);
		
		//Since the camera is dependant on the character position, we move it to the appropriate position and update the camera view.
		cam.position.set(pos.x, pos.y, 0);
		
		world.step(delta, 4, 4); //This command calls upon the World to calculate physics changes and affects the Bodys within accordingly
								 //The other parameters determine how many iterations are used in calculating the physics (higher = more accurate but slower)
		
		renderer.render(map.getObjects(), delta); //Arkitek Renderer - as used in Creation Mode
		
		player.getBody().setAwake(true);	//This ensures that physics will be enacted on the character if such physics exist (even if the world has calculated that none will)
		player.update(delta); 
		
		//This renders the player and is here instead of in the ArkitekRenderer object because the player only shows in Play Mode.
		playerBatch.setProjectionMatrix(cam.combined);
		playerBatch.enableBlending();
		playerBatch.begin();
		player.render(playerBatch);
		playerBatch.end();
		
		ui.draw(); //DRAWS USER INTERFACE AS CREATED IN show(), after the rest as been drawn so it ends up ON TOP.
		
		if (Gdx.input.isKeyPressed(Keys.ESCAPE)) {
			game.setScreen(new MainMenu(game));
		}
		
		if (Gdx.input.isKeyPressed(Keys.BACK)) {
			game.setScreen(new MainMenu(game));
		}
		
	}

	@Override
	public void hide() {
		renderer.dispose();
	}

	@Override
	public boolean keyDown(int keycode) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean keyTyped(char character) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean keyUp(int keycode) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean scrolled(int amount) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean touchDown(int x, int y, int pointer, int button) {
		ui.touchDown(x, y, pointer, button);
		Actor hitActor = hit(x, y);
		if (hitActor != null && hitActor.name != null) {
			if (hitActor.name.startsWith("left")) {
				// LEFT PANEL IS INDEX 0, RIGHT PANEL IS INDEX 1
				Button tempButton = (Button) ui.getActors().get(1);
				tempButton.setText("JUMP");
				player.leftDown = pointer;
				if (player.rightDown > -1 )
					player.jump = pointer;
			}
			if (hitActor.name.startsWith("right")) {
				Button tempButton = (Button) ui.getActors().get(0);
				tempButton.setText("JUMP");
				player.rightDown = pointer;
				if (player.leftDown > -1)
					player.jump = pointer;
			}
			return true;
		} else
			return false;
	}

	@Override
	public boolean touchDragged(int x, int y, int pointer) {
		ui.touchDragged(x, y, pointer);
		return false;
	}

	@Override
	public boolean touchMoved(int x, int y) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean touchUp(int x, int y, int pointer, int button) {
		ui.touchUp(x, y, pointer, button);
		
		if (player.leftDown == pointer) {
			Button tempButton = (Button) ui.getActors().get(1);
			tempButton.isChecked = false;
			tempButton.setText(">");
			player.leftDown = -1;
		}
		
		if (player.rightDown == pointer) {
			Button tempButton = (Button) ui.getActors().get(0);
			tempButton.isChecked = false;
			tempButton.setText("<");
			player.rightDown = -1;
		}
		
		if (player.jump > -1) {
			player.jump = -1;
		}
		
		Button leftButton = (Button) ui.getActors().get(0);
		leftButton.setText("<");

		Button rightButton = (Button) ui.getActors().get(1);
		rightButton.setText(">");
		
		return false;
	}

	public Actor hit(int x, int y) {
		for (Actor actor : ui.getActors()) {
			if (actor.x < x && x < actor.x + actor.width && actor.y < y && y < actor.y + actor.height) {
				return actor;
			}
		}
		return null;
	}
}